#!/bin/bash
# ./GENMCA status [processor]
SIMPLE=(
        [0]="No Error"
        [1]="Unclassified"
        [2]="Microcode ROM parity error"
        [3]="External error"
        [4]="FRC error"
        [5]="Internal parity error"
        [6]="SMM Handler Code Access Violation"
     )
LL=( "Level-0" "Level-1" "Level-2" "Level-3" "UNKNOWN" )
TT=( "Instruction" "Data" "Generic" "Unknown" )
mmm_mnemonic=( "GEN" "RD" "WR" "AC" "MS" "RES5" "RES6" "RES7" )
RRRR=(
        [0]="Generic"
        [1]="Read"
        [2]="Write"
        [3]="Data-Read"
        [4]="Data-Write"
        [5]="Instruction-Fetch"
        [6]="Prefetch"
        [7]="Eviction"
        [8]="Snoop"
     )
PP=(
	[0]="Local-CPU-originated-request"
	[1]="Responed-to-request"
	[2]="Observed-error-as-third-party"
	[3]="Generic"
   )
T=( "Request-did-not-timeout" "Request-timed-out" )

II=( "Memory-access" "Reserved" "IO" "Other-transaction" )

# generate the input files for 'mcelog --ascii'
creat_file()
{
	echo "$1" > $mca_fname
	echo "$2" >> $mca_fname
	echo "$3" >> $mca_fname
}

simple_error()
{
	if [[ "$ecode" -le 6 ]]; then
		echo "${SIMPLE[$ecode]}" > $mca_fexpect
	elif [[ "$ecode" -eq 0x0400 ]]; then
		echo "Internal Timer error" > $mca_fexpect
	elif [[ "$ecode" -gt 0x0400 ]]; then
		printf "Internal unclassified error: %x" $ecode > $mca_fexpect
	fi
}

generic_cache_hierarchy()
{
	local levelnum=$(($ecode & 3))

	echo "${LL[$levelnum]} Generic cache hierarchy error" > $mca_fexpect
}

tlb_errors()
{
	local levelnum=$(($ecode & 3))
	local typenum=$(($ecode >> 2 & 3))

	echo "${TT[$typenum]} TLB ${LL[$levelnum]} Error" > $mca_fexpect
}

memory_controller_errors()
{
	local chan=$(($ecode & 0xf))
	local mmm_num=$(($ecode >> 4 & 7))
	local mem_as_cache_bit=$(($ecode >> 9 & 1))

	if [[ "$chan" -eq 0xf ]]; then
		chan="unspecified"
	else
		chan=$(printf "%u" $chan)
	fi
	if [[ "$mem_as_cache_bit" -ne 0x1 ]]; then
		echo "MEMORY CONTROLLER ${mmm_mnemonic[mmm_num]}_CHANNEL${chan}_ERR" > $mca_fexpect
	else
		echo "Memory as cache: MEMORY CONTROLLER ${mmm_mnemonic[mmm_num]}_CHANNEL${chan}_ERR" > $mca_fexpect
	fi
}

cache_hierarchy_errors()
{
	local levelnum=$[$(($ecode & 3))+1]
	local typenum=$(($ecode >> 2 & 3))
	local rrrr_num=$(($ecode >> 4 & 0xf))
	local rrrr_str

	if [[ "$rrrr_num" -gt 8 ]]; then
		rrrr_str="UNKNOWN"
	else
		rrrr_str="${RRRR[$rrrr_num]}"
	fi
	echo "${TT[$typenum]} CACHE ${LL[$levelnum]} ${rrrr_str} Error" > $mca_fexpect
}

bus_and_interconnect_errors()
{
	local levelnum=$(($ecode & 3))
	local ii_num=$(($ecode >> 2 & 3))
	local rrrr_num=$(($ecode >> 4 & 0xf))
	local rrrr_str
	local t_num=$(($ecode >> 8 & 1))
	local pp_num=$(($ecode >> 9 & 3))

	if [[ "$rrrr_num" -gt 8 ]]; then
		rrrr_str="UNKNOWN"
	else
		rrrr_str="${RRRR[$rrrr_num]}"
	fi
	echo "${LL[$levelnum]} ${PP[$pp_num]} ${rrrr_str} ${II[ii_num]} ${T[t_num]}" > $mca_fexpect
}

usage()
{
echo "Usage: ${0##*/} status [processor]"
echo -e "\tstatus   	--- the 64-bit IA32_MCi_Status value, i.e., 0x8000000000000002."
echo -e "\tprocessor 	--- the format of 'processor' is 'vendor:CPUID', vendor: 0 - Intel,"
echo -e "\t         	    i.e., 0:0x50650 ."
	exit 1
}

complain()
{
	echo "Unknown MCA error code"
	exit 2
}

if [ "x$1" = "x" ]; then
	usage
fi

cpu_clause="CPU 0 1"
status_clause=$(printf "STATUS 0x%lx" $1)
proc_str=${2:-"0:0x50650"}
proc_clause="PROCESSOR ${proc_str}"
ecode=$(($1 & 0xffff))
# input file for 'mcelog --ascii'
mca_fname=$(printf "mca-%x" $ecode)
# file for saving the expected string parsed from
# raw MCA error codes.
mca_fexpect=${mca_fname}-expect

# mask F bit in MCA error code
if [[ "$ecode" -ge 0x1000 ]]; then
	ecode=$(($ecode & ~0x1000))
fi
if [[ "$ecode" -ge 0x0000 && "$ecode" -lt 0x0007 ]]; then simple_error
elif [[ "$ecode" -ge 0x0007 && "$ecode" -lt 0x000c ]]; then complain
elif [[ "$ecode" -ge 0x000c && "$ecode" -lt 0x0010 ]]; then generic_cache_hierarchy
elif [[ "$ecode" -ge 0x0010 && "$ecode" -lt 0x0020 ]]; then tlb_errors
elif [[ "$ecode" -ge 0x0020 && "$ecode" -lt 0x0080 ]]; then complain
elif [[ "$ecode" -ge 0x0080 && "$ecode" -lt 0x0100 ]]; then memory_controller_errors
elif [[ "$ecode" -ge 0x0100 && "$ecode" -lt 0x0200 ]]; then cache_hierarchy_errors
elif [[ "$ecode" -ge 0x0200 && "$ecode" -lt 0x0280 ]]; then complain
elif [[ "$ecode" -ge 0x0280 && "$ecode" -lt 0x0300 ]]; then memory_controller_errors
elif [[ "$ecode" -ge 0x0300 && "$ecode" -lt 0x0400 ]]; then complain
elif [[ "$ecode" -ge 0x0400 && "$ecode" -lt 0x0800 ]]; then simple_error
elif [[ "$ecode" -ge 0x0800 && "$ecode" -lt 0x1000 ]]; then bus_and_interconnect_errors
fi

creat_file "$cpu_clause" "$proc_clause" "$status_clause"
